package com.dhcc.rc.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.dhcc.rc.annotation.MyCacheable;
import com.dhcc.rc.dao.UserDao;
import com.dhcc.rc.entity.User;
import com.dhcc.rc.service.UserService;

@Service
@CacheConfig(cacheNames = { "myuser" })
public class UserServiceImpl implements UserService {
	@Autowired
	private UserDao userDao;

	// 1、分页查询
	@Override
	public Page findAll(Map<String, Object> map, int page, int size) {
		// 1.需要查询条件
		Specification<User> spec = new Specification<User>() {
			/**
			 * 动态拼接查询条件
			 * 
			 * @return
			 */
			public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery,
					CriteriaBuilder criteriaBuilder) {
				List<Predicate> list = new ArrayList<>();
				// 根据请求的companyId是否为空构造查询条件
				if (!StringUtils.isEmpty(map.get("name"))) {
					list.add(criteriaBuilder.equal(root.get("name").as(String.class), (String) map.get("name")));
				}
				return criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
			}
		};
		// 2.分页
		Page<User> pageUser = userDao.findAll(spec, new PageRequest(page - 1, size));
		return pageUser;
	}

	/**
	 * 1.保存用户
	 */
	@CachePut(key = "\"user_\" + #user.id")
	@Override
	public void save(User user) {
		user.setPassword("123456");// 设置初始密码
		// 调用dao save
		userDao.save(user);
	}

	/**
	 * 2.更新用户
	 */
	// 使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”
	@CachePut(key = "\"user_\" + #p0.id")
//    @CachePut(key = "\"user_\" + #user.id")
	@Override
	public void update(User user) {
		// 1.根据id查询部门
		User target = userDao.findById(user.getId()).get();
		// 2.设置部门属性
		target.setName(user.getName());
		// 3.更新部门
		userDao.save(target);
	}

	/**
	 * 3.根据id查询用户
	 */
	@Override
	@Cacheable(key = "\"user_\" + #id")
	public User findById(String id) {
		User user = userDao.findById(id).get();
		return user;
	}

	/**
	 * 4.查询用户
	 */
	@Override
	@Cacheable
	public List<User> findAll() {
		return userDao.findAll();
	}

	/*
	 * @CacheEvict
	 * 
	 * @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。 当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。
	 * 
	 * @CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。
	 * 其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的（
	 * 对应Cache的名称）；key表示需要清除的是哪个key，如未指定则会使用默认策略生成的key；condition表示清除操作发生的条件。
	 * 下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。
	 */
	/**
	 * 5.根据id删除用户
	 */
	/*
	 * beforeInvocation属性 清除操作默认是在对应方法成功执行之后触发的，即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。
	 * 使用beforeInvocation可以改变触发清除操作的时间， 当我们指定该属性值为true时，Spring会在调用该方法之前清除缓存中的指定元素。
	 */
	@Override
	@CacheEvict(key = "\"user_\" + #id", beforeInvocation = true)
	public void deleteById(String id) {
		userDao.deleteById(id);
	}

	/**
	 * 6.删除所有用户
	 */
	/*
	 * allEntries属性 allEntries是boolean类型，表示是否需要清除缓存中的所有元素。
	 * 默认为false，表示不需要。当指定了allEntries为true时，Spring Cache将忽略指定的key。
	 * 有的时候我们需要Cache一下清除所有的元素，这比一个一个清除元素更有效率
	 */
	@CacheEvict(allEntries = true)
	public void deleteAll() {
		userDao.deleteAll();
	}

	/*
	 * @Caching
	 * 
	 * @Caching注解可以让我们在一个方法或者类上同时指定多个Spring
	 * Cache相关的注解。其拥有三个属性：cacheable、put和evict，分别用于指定@Cacheable、@CachePut和@
	 * CacheEvict。
	 */
	@Caching(cacheable = @Cacheable("user1"), 
			 evict = { 
					 @CacheEvict("user2"),
					 @CacheEvict(value = "user3", allEntries = true) 
					 }
	)
	@Override
	public User findOne(String id) {
		User user = userDao.findById(id).get();
		return user;
	}

	@Caching(cacheable = @Cacheable("user2"), 
			 evict = { 
					 @CacheEvict("user3"),
					 @CacheEvict(value = "user1", allEntries = true) 
					 }
	)
	@Override
	public User findTwo(String id) {
		User user = userDao.findById(id).get();
		return user;
	}

	@Caching(cacheable = @Cacheable("user3"), 
			 evict = { 
					 @CacheEvict("user1"),
					 @CacheEvict(value = "user2", allEntries = true) 
					 }
	)
	@Override
	public User findThree(String id) {
		User user = userDao.findById(id).get();
		return user;
	}

	@MyCacheable
	@Override
	public User findFour(String id) {
		User user = userDao.findById(id).get();
		return user;
	}
}